package com.dianpingmedia.wifitz.tcp.client;

import android.util.Log;

import com.dianpingmedia.wifitz.tcp.dataObjectDef.ConvertLib;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;

public class MyTcpBase {
    protected static final int S_PACKET_LENGTH_BYTES = 2;

    class OutputRunnable implements Runnable {
        private boolean m_bRunnable;
        private MyTcpBase m_myTcpBase;
        private OutputStream m_outputStream = null;
        private byte[] m_byArraySize = new byte[4];
        private List<byte[]> m_listOutputBytes = new LinkedList<byte[]>();

        public OutputRunnable(OutputStream out, MyTcpBase myTcpBase) {
            this.m_outputStream = out;
            this.m_myTcpBase = myTcpBase;
            this.m_bRunnable = true;
        }

        public void release() {
            if (this.m_bRunnable) {
                this.m_bRunnable = false;
                synchronized (this.m_listOutputBytes) {
                    this.m_listOutputBytes.notify();
                }
            }
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub
            //synchronized(this.m_outputStream)
            //{
            //	this.m_outputStream.notify();
            //}
            byte[] byData = null;
            try {
                while (this.m_bRunnable) {
                    synchronized (this.m_listOutputBytes) {
                        //消费者模型，等待有数据时则取出一个处理
                        while (this.m_listOutputBytes.size() == 0) {
                            this.m_listOutputBytes.wait();
                            if (this.m_bRunnable == false) {
                                this.close();
                                return;
                            }
                        }
                        byData = this.m_listOutputBytes.remove(0);
                    }
                    if (this.write(byData, 0, byData.length) == false) {
                        this.close();
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 准备写数据，实际上是将数据先放到缓冲队列中
         *
         * @param byData
         */
        public void write(byte[] byData) {
            try {
                synchronized (this.m_listOutputBytes) {
                    this.m_listOutputBytes.add(byData);
                    this.m_listOutputBytes.notify();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 向Socket的远方传送数据
         *
         * @param byArrayData 要传送的数据的字节数组
         * @param iOffset     要传送的数据在字节数组byArrayData中的起始偏移值，如果从头开始，则值为0。
         * @param iCount      要传送的数据从iOffset开始计算起的有效长度。
         * @return true表示成功； false表示失败，失败时会自动关闭Socket连接。
         */
        public boolean write(byte[] byArrayData, int iOffset, int iCount) {
            try {
                this.m_outputStream.write(ConvertLib.intToBytesByBigEndian(iCount, MyTcpBase.S_PACKET_LENGTH_BYTES, this.m_byArraySize, 0), 0, MyTcpBase.S_PACKET_LENGTH_BYTES);
                this.m_outputStream.write(byArrayData, iOffset, iCount);
                this.m_outputStream.flush();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                this.close();
            }
            return false;
        }

        private void close() {
            if (this.m_myTcpBase == null) {
                return;
            }
            this.m_bRunnable = false;

            synchronized (this.m_listOutputBytes) {
                try {
                    this.m_listOutputBytes.notify();
                    this.m_listOutputBytes.clear();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            this.m_listOutputBytes = null;
            try {
                this.m_outputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.m_outputStream = null;
            this.m_byArraySize = null;
            //如果outputStream被关闭后，SOCKET也会被关闭的话，可以去掉this.m_myTcpBase这个属性
            try {
                this.m_myTcpBase.close();
                this.m_myTcpBase = null;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private InputStream m_inputStream = null;
    private Socket m_socket = null;
    private OutputRunnable m_outputRunnable = null;

    private int m_iPacketLength;
    private byte[] m_byArrayRecvData;

    public Socket getSocket() {
        return this.m_socket;
    }

    /**
     * 得到接收的数据的数组。有效数据从索引0开始，一直到read()方法返回的长度。
     *
     * @return 保存接收到的数据的数组
     */
    public byte[] getRecvBytes() {
        return this.m_byArrayRecvData;
    }

    /**
     * 得到接收的数据的字符串表示
     *
     * @return
     */
    public String getRecvString() {
        return new String(this.m_byArrayRecvData, 0, this.m_iPacketLength);
    }

    /**
     * 匹配一个已创建连接的socket
     *
     * @param socket
     * @return true表示匹配成功；false表示失败，该Socket未建立连接。
     */
    public boolean attachSocket(Socket socket) {
        if (socket == null) {
            return false;
        }
        if (socket.isConnected()) {
            try {
                this.m_inputStream = socket.getInputStream();
                OutputStream os = socket.getOutputStream();
                this.m_outputRunnable = new OutputRunnable(os, this);
                this.m_socket = socket;
                this.m_byArrayRecvData = new byte[1024];
                new Thread(this.m_outputRunnable).start();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                this.close();
            }
        }
        return false;
    }

    /**
     * 向Socket的远方传送数据
     *
     * @param byArrayData 要传送的数据的字节数组
     */
    public void write(byte[] byArrayData) {
        this.m_outputRunnable.write(byArrayData);
    }

    /**
     * 从Socket中读取数据。当返回值>0时，可以调用getRecvData()方法得到接收到的数据。
     *
     * @return 如果为-1，表示连接已经关闭。如果值>0，表示读取到了数据的长度。
     */
    public int read() {
        //首先读取包的长度，包的长度用3个字节表示
        try {
            int iOffset = 0;
            int iLength = 0;
            while (true) {
                //首先读取包的长度
                Log.d("MyTcpBase", "------------------1");
                iLength = this.m_inputStream.read(this.m_byArrayRecvData, iOffset, MyTcpBase.S_PACKET_LENGTH_BYTES - iOffset);
                if (iLength < 0) {
                    this.close();
                    return -1;
                }
                iOffset += iLength;
                if (iOffset >= MyTcpBase.S_PACKET_LENGTH_BYTES) {
                    //如果读取的长度足够
                    break;
                }
                //如果读取的内容不够
                //继续读取
            }

            //读取到了长度
            Log.d("MyTcpBase", "------------------iLength="+iLength);
            this.m_iPacketLength = ConvertLib.bytesToIntByBigEndian(this.m_byArrayRecvData, MyTcpBase.S_PACKET_LENGTH_BYTES);
            //检查接收数据的缓冲区的大小
            this.inspectRecvDataBytesSize();

            //接收数据
            iOffset = 0;
            while (true) {
                //循环读取数据
                iLength = this.m_inputStream.read(this.m_byArrayRecvData, iOffset, this.m_iPacketLength - iOffset);
                if (iLength < 0) {
                    this.close();
                    return -1;
                }
                iOffset += iLength;
                Log.d("MyTcpBase", "iOffset="+iOffset+" this.m_iPacketLength="+ this.m_iPacketLength);
                if (iOffset >= this.m_iPacketLength) {
                    //如果读取的长度足够
                    break;
                }
                //如果读取的内容不够
                //继续读取
            }
            return this.m_iPacketLength;
        } catch (Exception e) {
            this.close();
            return -1;
        }
    }

    /**
     * 关闭Socket连接。同时也会关闭InputStream和OutputStream。
     */
    public void close() {
        if (this.m_outputRunnable == null) {
            return;
        }
        try {
            if (this.m_socket != null) {
                this.m_socket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.m_socket = null;
        try {
            if (this.m_inputStream != null) {
                this.m_inputStream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.m_inputStream = null;
        this.m_byArrayRecvData = null;
        try {
            this.m_outputRunnable.release();
            this.m_outputRunnable = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 检查接收数据缓冲区的长度。如果太小，则创建一个足够大的缓冲区
     */
    private void inspectRecvDataBytesSize() {
        if (this.m_byArrayRecvData.length < this.m_iPacketLength) {
            this.m_byArrayRecvData = new byte[this.m_iPacketLength];
        }
    }
}
